package com.line.myprojectservice.chatserver.service;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.line.myprojectservice.utils.ContextUtil;
import com.line.myprojectservice.chatserver.pojo.WebSocketClient;
import com.line.myprojectservice.service.DoctorService;
import com.line.myprojectservice.service.PatientService;
import com.line.myprojectservice.service.impl.DoctorServiceImpl;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

@Slf4j
public class PublicService {


    private static final DoctorService doctorService = ContextUtil.getBean(DoctorServiceImpl.class);
    private static final PatientService patientService = ContextUtil.getBean(PatientService.class);


    /**
     * 记录所有连接
     * id：session
     */
    public static final Map<String, WebSocketClient> allConnectedMap = new ConcurrentHashMap<>();

    /**
     * 各部门病人队列
     * 诊室名：挂号队列<连接信息>
     */
    public static final Map<String, ConcurrentLinkedQueue<WebSocketClient>> patientsMap = new ConcurrentHashMap<>();


    /**
     * 各部门医生队列
     * 诊室名：空闲医生队列<连接信息>
     */
    public static final Map<String, ConcurrentLinkedQueue<WebSocketClient>> doctorsMap = new ConcurrentHashMap<>();

    /**
     * 医生和病人的 1对1 Map
     * 医生：病人
     */
    public static final Map<WebSocketClient, WebSocketClient> doctorPatientMap = new ConcurrentHashMap<>();

    /**
     * 病人和医生的 1对1 Map
     * 病人：医生
     */
    public static final Map<WebSocketClient, WebSocketClient> patientDoctorMap = new ConcurrentHashMap<>();

    /**
     * 患者发送的信息 转发到 医生
     */
    public static void forwardToDoctor(String userId, String message) {
        try {
            Channel doctorSession = patientDoctorMap.get(allConnectedMap.get(userId)).getChannel();
            sendMessage(message, doctorSession);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }

    /**
     * 医生发送的信息 转发到 患者
     */
    public static void forwardToPatient(String userId, String message) {
        try {
            Channel patientSession = doctorPatientMap.get(allConnectedMap.get(userId)).getChannel();
            sendMessage(message, patientSession);

        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }


    /**
     * 推送至  指定客户端
     * 所有线程共用这个方法，会在这阻塞吗？，还是说拷贝，与方法在哪里无关
     */
    public static void sendMessage(String message, Channel channel) {
        try {

            //如果医生下线了发不过去会报异常吗
            //getBasicRemote这个方法用同步还是异步

            if (message != null) {

                channel.writeAndFlush(new TextWebSocketFrame(message));


                log.info("调用发送信息，给{}发送消息，内容：{}",channel.id(), message);
            } else {
                log.error("空消息=====服务端发送消息给客户端失败");
            }
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }

    /**
     * 通用 连接释放方法
     *
     * @param departmentName 科室名
     * @param userId         医生/患者 调度者Id
     */
    public static void release(String departmentName, String userId) {

        JSONObject jsonObject = new JSONObject();
        jsonObject.set("signal", "断开");

        //医生或者是患者
        WebSocketClient webSocketClient = allConnectedMap.get(userId);

        //判是否为空？  还是try合适


        //存一下，否者直接按id插，转发方法，因为以为已经实现删除了  ,其中一个为null，不过在if中排除了
        Channel   doctorSession = null;
        Channel   patientSession = null;
        if (patientDoctorMap.get(allConnectedMap.get(userId)) != null) {
            doctorSession = patientDoctorMap.get(allConnectedMap.get(userId)).getChannel();
        }
        if (doctorPatientMap.get(allConnectedMap.get(userId)) != null) {
            patientSession = doctorPatientMap.get(allConnectedMap.get(userId)).getChannel();

        }


        //先关闭，再释放到队列      允许删除 空值
        patientDoctorMap.remove(webSocketClient);
        doctorPatientMap.remove(webSocketClient);


        //释放医生到空闲队列
        if (webSocketClient.getIdentity().equals("患者")) {

            //前端控制信号
//            forwardToDoctor(userId, jsonObject.toString());
            if (doctorSession != null) {
                sendMessage(jsonObject.toString(), doctorSession);
            }


            WebSocketClient doctorClient = patientDoctorMap.get(webSocketClient);

            allConnectedMap.remove(userId);

            //是否拿到医生
            if (doctorClient != null) {
//                //释放医生
//                doctorsMap.get(departmentName).offer(doctorClient);
                //总链接中尝试删除
                allConnectedMap.remove(doctorClient.getUserId());
            }
            //挂号等待途中 患者端断开连接的情况，在调度时处理


        } else {

            //前端控制信号
//            forwardToDoctor(userId, jsonObject.toString());
            if (patientSession != null) {
                sendMessage(jsonObject.toString(), patientSession);
            }

//            doctorsMap.get(departmentName).offer(webSocketClient);
            WebSocketClient patientClient = doctorPatientMap.get(webSocketClient);
            allConnectedMap.remove(userId);
            if (patientClient != null) {
                allConnectedMap.remove(patientClient.getUserId());
            }
        }

        //调度
        dispatch(departmentName);


    }

    /**
     * 通用 调度服务
     */
    public static void dispatch(String departmentName) {

        WebSocketClient doctorClient = doctorsMap.get(departmentName).poll();
        if (doctorClient != null) {
            //只有先确认拿到医生，才能动患者，否则破坏挂号顺序
            WebSocketClient patientClient = patientsMap.get(departmentName).poll();
            if (patientClient != null) {

                if (allConnectedMap.containsKey(patientClient.getUserId())) {
                    //建立连接
                    doctorPatientMap.put(doctorClient, patientClient);
                    //为了使调度方法不返回doctoer实例，以便通过 V查K
                    patientDoctorMap.put(patientClient, doctorClient);

                    //对象直接转json 字符串 且格式化
                    String doctorJsonString = JSONUtil.toJsonPrettyStr(doctorService.queryDoctorVoById(doctorClient.getUserId()));
                    String patientJsonString = JSONUtil.toJsonPrettyStr(patientService.queryPatientVoById(patientClient.getUserId()));

                    // 前端放开
                    sendMessage(doctorJsonString, patientClient.getChannel());

                    log.info(patientClient.getChannel().id().toString()+"+++++");

                    sendMessage(patientJsonString, doctorClient.getChannel());

                    log.info(doctorClient.getChannel().id().toString()+"+++++");


                } else {
                    //挂号后用户刷新调用释放，挂号队列中的实例失效的问题
                    //调度下一位
                    dispatch(departmentName);
                }

            } else {
                // 挂号队列为空
                // 没有人看病 / 或别人在结束方法调度时，把自己调度走了，所以把医生还回去
                doctorsMap.get(departmentName).offer(doctorClient);

                JSONObject jsonObject = new JSONObject();
                jsonObject.set("signal", "现在没人看病");

                sendMessage(jsonObject.toString(), doctorClient.getChannel());
            }
        } else {
            //没有医生可以分配，患者继续等待
            //医生调用时，则是自己被被人调度走了

            WebSocketClient patientClient = patientsMap.get(departmentName).peek();
            if (patientClient != null) {

                JSONObject jsonObject = new JSONObject();
                jsonObject.set("signal", "现在没医生");

                sendMessage(jsonObject.toString(), patientClient.getChannel());
            }


        }


    }


    /**
     * 初始化departmentmap，一开始必须有所有科室的空挂号队列
     */
    public static void initDepartment() {
        if (patientsMap.isEmpty()) {
            init(patientsMap);
        }
        if (doctorsMap.isEmpty()) {
            init(doctorsMap);
        }
    }

    private static void init(Map<String, ConcurrentLinkedQueue<WebSocketClient>> map) {
        map.put("皮肤科", new ConcurrentLinkedQueue<>());
        map.put("口腔科", new ConcurrentLinkedQueue<>());
        map.put("耳鼻喉科", new ConcurrentLinkedQueue<>());
        map.put("心血管内科", new ConcurrentLinkedQueue<>());
        map.put("精神科", new ConcurrentLinkedQueue<>());
        map.put("营养保健科", new ConcurrentLinkedQueue<>());
        map.put("牙科", new ConcurrentLinkedQueue<>());
        map.put("儿科", new ConcurrentLinkedQueue<>());
    }


    //心跳机制，把医生拿跑了，自己崩溃了，医生回不来了


}
